import {ShaderMaterial,Texture} from 'three';
import Constant from './ui-constant';
import UI_TEXT_VERTEX_SHADER from '../../shaders/ui-text-vertex-shader';
import UI_TEXT_FRAGMENT_SHADER from '../../shaders/ui-text-fragment-shader';
import UI_BACKGROUND_VERTEX_SHADER from '../../shaders/ui-background-vertex-shader';
import UI_BACKGROUND_FRAGMENT_SHADER from '../../shaders/ui-background-fragment-shader';

const COMPARE_CLIP=(ps0,ps1)=>{
    if((!ps0)!=(!ps1))return false;
    if(ps0.length!=ps1.length)return false;
    const THRES=1e-6;
    for(let i=0;i<ps0.length;i++){
        const p0=ps0[i];
        const p1=ps1[i];
        if(Math.abs(p0.constant-p1.constant)>THRES)return false;
        const n0=p0.normal;
        const n1=p1.normal;
        if(Math.abs(n0.x-n1.x)>THRES)return false;
        if(Math.abs(n0.y-n1.y)>THRES)return false;
        if(Math.abs(n0.z-n1.z)>THRES)return false;
    }
    return true;
};

export default function UIMaterial(Base=class {}){
    return class UIMaterial extends Base{
        
        getBackgroundUniforms() {
            let color, opacity;
            const texture = this.getBackgroundTexture();
            this.tSize.set(
                texture.image.width,
                texture.image.height
            );
            if ( texture.isDefault ) {
                color = this.getBackgroundColor();
                opacity = this.getBackgroundOpacity();
            } else {
                color = this.backgroundColor || Constant.DEFAULT[Constant.STYLE_BACKGROUND_WHITE_COLOR];
                opacity = ( !this.backgroundOpacity && this.backgroundOpacity !== 0 ) ?
                    Constant.DEFAULT[Constant.STYLE_BACKGROUND_OPAQUE_OPACITY] :
                    this.backgroundOpacity;
            }
            const backgroundMapping = ( () => {
                switch ( this.getBackgroundSize() ) {
                    case Constant.VALUE_STRETCH:
                        return 0;
                    case Constant.VALUE_CONTAIN: 
                        return 1;
                    case Constant.VALUE_COVER: 
                        return 2;
                }
            } )();
            return {
                texture,
                color,
                opacity,
                backgroundMapping,
                borderRadius: this.getBorderRadius(),
                borderWidth: this.getBorderWidth(),
                borderColor: this.getBorderColor(),
                borderOpacity: this.getBorderOpacity(),
                size: this.size,
                tSize: this.tSize
            }
        }
        updateBackgroundMaterial() {
            if ( this.backgroundUniforms ) {
                const uniforms = this.getBackgroundUniforms();
                this.backgroundUniforms.u_texture.value = uniforms.texture;
                this.backgroundUniforms.u_color.value = uniforms.color;
                this.backgroundUniforms.u_opacity.value = uniforms.opacity;
                this.backgroundUniforms.u_backgroundMapping.value = uniforms.backgroundMapping;
                this.backgroundUniforms.u_size.value = uniforms.size;
                this.backgroundUniforms.u_tSize.value = uniforms.tSize;
                if (Array.isArray(uniforms.borderRadius)) {
                    this.backgroundUniforms.u_borderRadiusTopLeft.value = uniforms.borderRadius[0];
                    this.backgroundUniforms.u_borderRadiusTopRight.value = uniforms.borderRadius[1];
                    this.backgroundUniforms.u_borderRadiusBottomRight.value = uniforms.borderRadius[2];
                    this.backgroundUniforms.u_borderRadiusBottomLeft.value = uniforms.borderRadius[3];
                } else {
                    this.backgroundUniforms.u_borderRadiusTopLeft.value = uniforms.borderRadius;
                    this.backgroundUniforms.u_borderRadiusTopRight.value = uniforms.borderRadius;
                    this.backgroundUniforms.u_borderRadiusBottomRight.value = uniforms.borderRadius;
                    this.backgroundUniforms.u_borderRadiusBottomLeft.value = uniforms.borderRadius;
                }
                this.backgroundUniforms.u_borderWidth.value = uniforms.borderWidth;
                this.backgroundUniforms.u_borderColor.value = uniforms.borderColor;
                this.backgroundUniforms.u_borderOpacity.value = uniforms.borderOpacity;
            }
        }
        updateTextMaterial(char) {
            if ( this.textUniforms ) {
                console.log('needs to modify!');
                this.textUniforms.u_texture.value = this.getFontTexture();
                this.textUniforms.u_color.value = this.getFontColor();
                this.textUniforms.u_opacity.value = this.getFontOpacity();
                this.textUniforms.u_pxRange.value = this.getFontPXRange();
                this.fontMaterial.u_useRGSS = this.getFontSupersampling();
            }
        }
        updateClippingPlanes( value ) {
            const newClippingPlanes = value !== undefined ? value : this.getClippingPlanes();
            if ( !COMPARE_CLIP(newClippingPlanes,this.clippingPlanes)) {
                this.clippingPlanes = newClippingPlanes;
                if ( this.fontMaterial ) {
                    for(let material of this.fontMaterial){
                        material.clippingPlanes = this.clippingPlanes;
                    }
                }
                if ( this.backgroundMaterial ) this.backgroundMaterial.clippingPlanes = this.clippingPlanes;
            }
        }
        getBackgroundMaterial() {
            const newUniforms = this.getBackgroundUniforms();
            if ( !this.backgroundMaterial || !this.backgroundUniforms ) {
                this.backgroundMaterial = this._makeBackgroundMaterial( newUniforms );
                return this.backgroundMaterial;
            }
            let borderRadiusChanged;
            if (Array.isArray(newUniforms.borderRadius)) {
                borderRadiusChanged = (
                  newUniforms.borderRadius[0] !== this.backgroundUniforms.u_borderRadiusTopLeft.value ||
                  newUniforms.borderRadius[1] !== this.backgroundUniforms.u_borderRadiusTopRight.value ||
                  newUniforms.borderRadius[2] !== this.backgroundUniforms.u_borderRadiusBottomRight.value ||
                  newUniforms.borderRadius[3] !== this.backgroundUniforms.u_borderRadiusBottomLeft.value
                )
            } else {
                borderRadiusChanged = (
                  newUniforms.borderRadius !== this.backgroundUniforms.u_borderRadiusTopLeft.value ||
                  newUniforms.borderRadius !== this.backgroundUniforms.u_borderRadiusTopRight.value ||
                  newUniforms.borderRadius !== this.backgroundUniforms.u_borderRadiusBottomRight.value ||
                  newUniforms.borderRadius !== this.backgroundUniforms.u_borderRadiusBottomLeft.value
                )
            }
            if (
              newUniforms.texture !== this.backgroundUniforms.u_texture.value ||
              newUniforms.color !== this.backgroundUniforms.u_color.value ||
              newUniforms.opacity !== this.backgroundUniforms.u_opacity.value ||
              newUniforms.backgroundMapping !== this.backgroundUniforms.u_backgroundMapping.value ||
              borderRadiusChanged ||
              newUniforms.borderWidth !== this.backgroundUniforms.u_borderWidth.value ||
              newUniforms.borderColor !== this.backgroundUniforms.u_borderColor.value ||
              newUniforms.borderOpacity !== this.backgroundUniforms.u_borderOpacity.value ||
              newUniforms.size !== this.backgroundUniforms.u_size.value ||
              newUniforms.tSize !== this.backgroundUniforms.u_tSize.value
            ) {
                this.updateBackgroundMaterial();
            }
            return this.backgroundMaterial;
        }
        getFontMaterial(charObj,index) {
            const newUniforms = {
                'u_texture': charObj.texture,
                'u_color': this.getFontColor(),
                'u_opacity': this.getFontOpacity(),
                'u_pxRange': this.getFontPXRange(),
                'u_useRGSS': this.getFontSupersampling()
            };
            this.fontMaterial=this.fontMaterial||[];
            const material=this._makeTextMaterial( newUniforms );
            this.fontMaterial[index] = material;
            return material;
        }
        generateCharTexture(charObj){
            const char=charObj.char;
            const height=charObj.height;
            const canvas=document.createElement('canvas');
            const context=canvas.getContext('2d');

            const fontSize=this.getFontSize()*2;
            const ratio=height/fontSize;
            const fontWeight=this.getFontWeight();
            const fontFamily=this.getFontFamily();
            const fontStyle=fontWeight+' '+fontSize+'px '+fontFamily;
            context.font=fontStyle;
            const mw=context.measureText(char).width;

            charObj.width=mw*ratio*0.909091;
            canvas.width=mw;
            canvas.height=fontSize*1.1;
            
            context.font=fontStyle;
            context.fillStyle='#ffffff';
            context.textBaseline=this.getTextBaseLine();
            context.fillText(char,0,fontSize*0.55);
            const tex=new Texture(canvas);
            tex.needsUpdate=true;
            charObj.texture=tex;

            return charObj;
        }

        _makeTextMaterial( materialOptions ) {
            this.textUniforms = {
                'u_texture': { value: materialOptions.u_texture },
                'u_color': { value: materialOptions.u_color },
                'u_opacity': { value: materialOptions.u_opacity },
                'u_pxRange': { value: materialOptions.u_pxRange },
                'u_useRGSS': { value: materialOptions.u_useRGSS }
            }
            return new ShaderMaterial({
                uniforms: this.textUniforms,
                transparent: true,
                clipping: true,
                vertexShader: UI_TEXT_VERTEX_SHADER,
                fragmentShader: UI_TEXT_FRAGMENT_SHADER,
                extensions: {
                    derivatives: true
                }
            })
        }
        _makeBackgroundMaterial( materialOptions ) {
            this.backgroundUniforms = {
                'u_texture': { value: materialOptions.texture },
                'u_color': { value: materialOptions.color },
                'u_opacity': { value: materialOptions.opacity },
                'u_backgroundMapping': { value: materialOptions.backgroundMapping },
                'u_borderWidth': { value: materialOptions.borderWidth },
                'u_borderColor': { value: materialOptions.borderColor },
                'u_borderRadiusTopLeft': { value: 0 },
                'u_borderRadiusTopRight': { value: 0 },
                'u_borderRadiusBottomRight': { value: 0 },
                'u_borderRadiusBottomLeft': { value: 0 },
                'u_borderOpacity': { value: materialOptions.borderOpacity },
                'u_size': { value: materialOptions.size },
                'u_tSize': { value: materialOptions.tSize }
            };
            if (Array.isArray(materialOptions.borderRadius)) {
                this.backgroundUniforms['u_borderRadiusTopLeft'].values = materialOptions.borderRadius[0];
                this.backgroundUniforms['u_borderRadiusTopRight'].values = materialOptions.borderRadius[1];
                this.backgroundUniforms['u_borderRadiusBottomRight'].values = materialOptions.borderRadius[2];
                this.backgroundUniforms['u_borderRadiusBottomLeft'].values = materialOptions.borderRadius[3];
            } else {
                this.backgroundUniforms['u_borderRadiusTopLeft'].values = materialOptions.borderRadius;
                this.backgroundUniforms['u_borderRadiusTopRight'].values = materialOptions.borderRadius;
                this.backgroundUniforms['u_borderRadiusBottomRight'].values = materialOptions.borderRadius;
                this.backgroundUniforms['u_borderRadiusBottomLeft'].values = materialOptions.borderRadius;
            }
            return new ShaderMaterial({
                uniforms: this.backgroundUniforms,
                transparent: true,
                clipping: true,
                vertexShader: UI_BACKGROUND_VERTEX_SHADER,
                fragmentShader: UI_BACKGROUND_FRAGMENT_SHADER,
                extensions: {
                    derivatives: true
                }
            });
        }

    }
}